﻿using System;
using RayDen.Library.Components.Surface.Microfacet;
using RayDen.Library.Core;
using RayDen.Library.Core.Primitives;

namespace RayDen.Library.Components.Surface
{
    using Spectrum = RgbSpectrum;

    [Serializable]
    public class FresnelBlend : SurfaceBsdf
    {
        private Spectrum Rd, Rs;
        private readonly MicrofacetDistribution distribution;

        public FresnelBlend(Spectrum rs, Spectrum rd, float e)
        {
            distribution = 
                //new Anisotropic(10,10);
                //new Anisotropic(e*rs.Average, e*rd.Average);
                new BlinnDistribution(e);
            Rd = rd;
            Rs = rs;
        }

        protected override void EvalParams(ref SurfaceTextureData surfaceData)
        {
            Rd = surfaceData.Diffuse;
            Rs = surfaceData.Specular;
        }

        public override void f(ref Vector wo, ref Vector wi, ref Normal N, ref RgbSpectrum in_fs, out RgbSpectrum fs)
        {
            CreateFrame(ref N);
            var lwo = WorldToLocal(ref wo);
            var lwi = WorldToLocal(ref wi);

            Spectrum diffuse = (28f/(23f*MathLab.M_PI))*Rd*
                               (Spectrum.UnitSpectrum() - Rs)*
                               (1f - (float) MathLab.Pow(1 - .5*Math.Abs(CosTheta(ref lwi)), 5))*
                               (1f - (float) MathLab.Pow(1 - .5*Math.Abs(CosTheta(ref lwo)), 5));
            Vector H = Vector.Normalize(lwi + lwo);
            Spectrum specular = distribution.D(ref H)/
                                (8f*MathLab.M_PI*Vector.AbsDot(ref lwi, ref H)*
                                 Math.Max(Math.Abs(CosTheta(ref lwi)), Math.Abs(CosTheta(ref lwo)))) *
                                FresnelController.Schlick_FresnelTerm(Rs, Vector.Dot(ref lwi, ref H));

            fs = diffuse + specular;
        }


        public override BrdfType Type
        {
            get { return BrdfType.Glossy | BrdfType.Diffuse; }
        }

        public override bool IsDiffuse()
        {
            return true;
        }

        public override bool IsSpecular()
        {
            return false;
        }
    }
}